package no.hib.dat102;

import no.hib.dat102.adt.MediaADT;
import no.hib.dat102.adt.MediaArchiveADT;

public class MediaArchive3<T> implements MediaArchiveADT<T> {
	private int amount;
	private LinearNode<T> archive;

	/**
	 * Creates an empty linked list
	 */
	public MediaArchive3() {
		amount = 0;
		archive = null;
	}

	/**
	 * Get a trimmed table of media objects from archive
	 * @return Table of media objects
	 */

	public Object[] GetTable() {

		// create return Object table that can hold all the elements
		Object[] tempTable = new Object[amount];

		if (archive == null) {
			return null;
		} else {
			LinearNode<T> thisNode = archive;
			int i = 0;
			while (thisNode != null) {
				tempTable[i] = thisNode.getmedia();
				i++;
				thisNode = thisNode.getNext();
			}
			// tempTable should now be filled up
			return tempTable;
		}
	}

	/**
	 * Adds the specified media to the top of this linked list
	 * @param media to be added to linked list
	 */
	public void Add(T media) {
		LinearNode<T> temp = new LinearNode<T>(media);

		temp.setNext(archive);
		archive = temp;
		amount++;
	}

	/**
	 * Deletes media with given ID number
	 * @return false for sucessful operation, true for error
	 * @throws EmptyCollectionException
	 *             if the stack is empty
	 */

	public boolean Delete(int number) throws EmptyCollectionException {
		if (isEmpty())
			throw new EmptyCollectionException("stack");

		LinearNode<T> thisNode = archive;
		LinearNode<T> previousNode = null;
		MediaADT element;
		boolean elementNotFound = true;

		// Delete case: First node
		element = (MediaADT) thisNode.getmedia();
		if (element.getNumber() == number) {
			archive = thisNode.getNext();
			elementNotFound = false;
		}

		// Delete case: Not first node
		previousNode = thisNode.getNext();
		thisNode = thisNode.getNext();

		while (thisNode != null && elementNotFound) {
			element = (MediaADT) thisNode.getmedia();
			if (element.getNumber() == number) {
				previousNode.setNext(thisNode.getNext());
				elementNotFound = false;
			}
			previousNode = thisNode.getNext();
			thisNode = thisNode.getNext();
		}
		if (elementNotFound == true) {
			System.out.println("Number not found, no media deleted");
			return true; // unsucessful operation
		} else {
			System.out.println("Media with number " + number + " deleted");
			return false; // sucessful operation
		}
	}

	/**
	 * Search and return trimmed table of media objects from partial title string (match any part of title)
	 * @param partialstring to compare titles with
	 * @return table of media objects
	 */
	public Object[] SearchTitle(String partialString) {

		Object[] hitsTable = new Object[amount];
		int hitsTablePosition = 0;
		LinearNode<T> thisNode = archive;
		MediaADT element;
		String titleString;
		
		while (thisNode != null) {
			element = (MediaADT) thisNode.getmedia();
			titleString = element.getTitle();
			if (titleString.matches("(.*)"+partialString+"(.*)")) { //if there is a partial match
				hitsTable[hitsTablePosition] = element; //then copy over the object to hitsTable
				hitsTablePosition++;
			}
			thisNode = thisNode.getNext();
		}
		return TrimTable(hitsTable);
	}

	/**
	 * Search and return trimmed table of media objects from partial artist
	 * string (match any part of artist)
	 * 
	 * @param partialstring
	 *            to compare artists with
	 * @return table of media objects
	 */
	public Object[] SearchArtist(String partialString) {
		
		Object[] hitsTable = new Object[amount];
		int hitsTablePosition = 0;
		LinearNode<T> thisNode = archive;
		MediaADT element;
		String artistString;
		
		while (thisNode != null) {
			element = (MediaADT) thisNode.getmedia();
			artistString = element.getArtist();
			if (artistString.matches("(.*)"+partialString+"(.*)")) { //if there is a partial match
				hitsTable[hitsTablePosition] = element; //then copy over the object to hitsTable
				hitsTablePosition++;
			}
			thisNode = thisNode.getNext();
		}
		return TrimTable(hitsTable);
		
	}

	/**
	 * Get the amount of media objects in the MediaArchive
	 * 
	 * @return number(int) of media objects
	 */
	public int GetAmountGenre(Genre genre) {

		int genreNumber = genre.getValue(); //convert genre enum to int
		int genreHits = 0;
		LinearNode<T> thisNode = archive;
		MediaADT element;
		Genre elementGenre;
		
		while (thisNode != null) {
			element = (MediaADT) thisNode.getmedia();
			elementGenre = element.getGenre();
			if (genreNumber == elementGenre.getValue()) {
				genreHits++;
			}
			thisNode = thisNode.getNext();
		}
		return genreHits;
	}

	/**
	 * Returns true if this linked list is empty, else false
	 * @return true if stack is empty
	 */
	public boolean isEmpty() {
		if (amount == 0) {return true;}
		else {return false;}
	}

	/**
	 * Returns the number of medias in this linked list
	 * @return number of medias in the linked list
	 */
	public int GetAmount() {
		return amount;
	}

	/**
	 * Returns a string representation of this linked list
	 * @return string representation of the linked list
	 */
	public String toString() {
		String result = "";
		LinearNode current = archive;

		while (current != null) {
			result = result + (current.getmedia()).toString() + "\n";
			current = current.getNext();
		}

		return result;
	}
	
	public Object[] TrimTable(Object[] archive) {
		
		int objects = 0;	//number of non-empty slots in the table
		int tempTablePosition = 0;
		for (int i=0; i<archive.length;i++) {	//go through the table
			if (archive[i] != null) {
				objects++;	//and count how many non-empty slots there are
			}
		}
		Object[] tempTable = new Object[objects];	//then make a tempTable the correct size to hold them all
		
		for(int i=0; i<archive.length; i++) { //go through each slot in the table and...
			if (archive[i] != null) { //if that position isn't empty in the archive
				tempTable[tempTablePosition] = archive[i]; //copy it over to the tempTable
				tempTablePosition++; //...and get ready to receive the next object
			}
		}
		return tempTable;
	}
	
	/**
	 * Search archive for object with a given ID number
	 * @param searchNr, number to search for
	 * @return mediaObject that has this ID number (or null if not found)
	 */
	//Search for a number and return that object
	public MediaADT SearchNr(int searchNr) {
		
		int mediaNr;
		
		LinearNode<T> thisNode = archive;
		MediaADT element;
		Genre elementGenre;
		boolean numberNotFound = true;
		
		while (thisNode != null && numberNotFound) {
			element = (MediaADT) thisNode.getmedia();
			mediaNr = element.getNumber();
			if (mediaNr == searchNr) {
				numberNotFound = false;
				return element;
			} 
		}
		return null; 	//no media with that number
	}
	
	/**
	 * Find first available ID number
	 * @return freeID, first available ID number
	 */
	public int GetFreeNr() {
		int freeID = -1;
		
		//TODO probably inefficient search
		int i = 0;
		while (SearchNr(i) != null) {
			i++;
		}
		freeID = i;
		if (freeID == -1) {System.out.println("ERROR - No free number found, this should be impossible"); }
		return freeID;
	}
}